package SimulationController;

import Entities.Car;
import PoolThread.ThreadPool;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import Segments.*;
import Util.ParseXMLFile;
import event.*;
import java.io.BufferedReader;
import java.io.InputStreamReader;
//Import necessary log4j API classes
import org.apache.log4j.*;

public class StreetControllerAgent {
    /* get a static logger instance with name
    com.foo.sampleapp.MyClass   */

    static Logger myLogger =
            Logger.getLogger(StreetControllerAgent.class.getName());
    Appender myAppender;
    SimpleLayout myLayout;
    private static boolean isActive;
    private static boolean isReady;
    private int index;
    private String name;
    int EMPTY = -1;
    int VERDE = 1;
    int ROJO = 0;
    int AMBAR = 3;
    

    Segment[] segments = new Segment[1000];
    Event rootControl = new Event();
    public int n;
    private Car car;

    /*
     * Empty constructor for ProActive
     */
    public StreetControllerAgent() {
        /* Set logger priority level programmatically. Though this is
        better done externally  */
        // myLogger.setLevel(Level.INFO);
        PropertyConfigurator.configure("C:\\Users\\emmanuel\\Documents\\NetBeansProjects\\CiudadelaSim\\src\\newproperties.properties");



        this.name = "Agent " + index;
        rootControl.setTime(0.0);
        rootControl.izquierdo = rootControl;
        rootControl.derecho = rootControl;
        rootControl.anterior = rootControl;
        rootControl.siguiente = rootControl;
        isActive = true;
        isReady = true;
        n = 0; //id de los carritos

    }

    public void setReady() {
        isReady = true;
        /* This log request enabled and log statement logged, since
        INFO = INFO */
        myLogger.info("The streetController is ready:" + isReady);

    }

    @Override
    public String toString() {
        return this.name;
    }

    public void initSimulation() throws IOException {
        double time = 0.0;
        Event source;
        //first we empty the RequestQueue
        //int id,int position,String name,int firstDelay
        //Car kdvu = new Car(2000,0,"MiCarro",300);
        new ParseXMLFile(segments, rootControl);
        //System.out.println(segments[0].getSize());
        //System.out.println(segments[1].getSize());
        FileOutputStream out = null;
        try {
            out = new FileOutputStream("c:/temp/log.txt", true);
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }

        /*
         *String objectID;
        double evTime;
        int segment;
        double evPos;
        int evType;
         */

        PrintWriter miArchivo = new PrintWriter(out);
        miArchivo.write("objectID,");
        miArchivo.write("evTime,");
        miArchivo.write("segment,");
        miArchivo.write("evPos,");
        miArchivo.write("evType,");
        miArchivo.println();
        miArchivo.close();

        // Stimulus initialization Car generation
        for (int i = 0; i <= 4; i++) {
            car = new Car(this.n, 0.0, "car " + this.n, 2.0, 40000.0, 2.0);

            this.putEvent(i, 0.0, Event.AE, car, 0, AMBAR, 0);
            this.n++;
        }
        car = new Car(this.n, 0.0, "car " + this.n, 2.0, 40000.0, 2.0);
        this.putEvent(35, 0.0, Event.AE, car, 0, AMBAR, 0);
        this.n++;
        
        //  Stimulus initialization Traffic Light generation
        double t;
        double f1 = 70;
        double f2 = 30;
        double f = f1 + f2;

        double frs1 = f1;
        double fas1;
        double fvs1 = fas1 = f2 / 2;
        double fvs2;
        double fas2 = fvs2 = frs1 / 2;
        double frs2 = f2;
        t = 1;
        do {
            this.putEvent(0, t, Event.LCE, "S1", 132.0, ROJO, 0);
            this.putEvent(35, t, Event.LCE, "S2", 132.0, VERDE, 0);
            this.putEvent(22, t, Event.LCE, "S3", 132.0, VERDE, 0);
            t = t + fas2;
            this.putEvent(35, t, Event.LCE, "S2", 132.0, AMBAR, 0);
            this.putEvent(22, t, Event.LCE, "S3", 132.0, AMBAR, 0);
            t = t + fvs2;
            this.putEvent(0, t, Event.LCE, "S1", 132.0, VERDE, 0);
            this.putEvent(35, t, Event.LCE, "S2", 132.0, ROJO, 0);
            this.putEvent(22, t, Event.LCE, "S3", 132.0,ROJO, 0);
            t = t + fvs1;
            this.putEvent(0, t, Event.LCE, "S1", 132.0, AMBAR, 0);
            t = t + fas1;
            if (t < 600) {
                f1 = 70;
                f2 = 30;
                f = f1 + f2;
                frs1 = f1;
                fvs1 = fas1 = f2 / 2;
                fas2 = fvs2 = frs1 / 2;
                frs2 = f2;
            }
            if (t >= 600) {
                f1 = 30;
                f2 = 70;
                f = f1 + f2;
                frs1 = f1;
                fvs1 = fas1 = f2 / 2;
                fas2 = fvs2 = frs1 / 2;
                frs2 = f2;
            }
            if (t >= 1200 && t < 2400) {
                f1 = 50;
                f2 = 50;
                f = f1 + f2;

                frs1 = f1;

                fvs1 = fas1 = f2 / 2;

                fas2 = fvs2 = frs1 / 2;
                frs2 = f2;
            }
        } while (t <= 2400);


        BufferedReader teclado = new BufferedReader(
                new InputStreamReader(System.in));



        try {
            ThreadPool pool = new ThreadPool(3);

            while (isActive) {



                if (isReady) {

                    //sendMessageToControler("Son las: "+rootControl.evTime());
                    if (rootControl.derecho != rootControl) {
                        //sendMessageToControler("hay muchos eventos");
                        source = sacarEvento();
                        myLogger.info("Found at: " + time + " event type:" + source.getEventType() + "to be executed at:   " + source.evTime());
                        //teclado.read();
                        // System.out.println("El evento que esta por ejecutarse es"+source.getEventType()+" a las "+source.evTime());

                        // System.out.println("El evento que esta por ejecutarse es"+source.getEventType()+" a las "+source.evTime());


                        if (source.evTime() < time) {

                            myLogger.error("At> " + time + "Error tipo evento:" + source.getEventType() + "At time> " + source.evTime());
                            throw new UnsupportedOperationException("new event is newer than actual time");
                        }
                        rootControl.setTime(source.evTime());
                        time = source.evTime();
                        if (source instanceof CarArrival) {
                            CarArrival evento = (CarArrival) source;
                            evento.putStreetAgent(this);
                            //evento = null;

                            myLogger.debug("Send " + evento + " to be executed by pollThread");
                            isReady = false;
                            pool.execute(evento);

                        }
                        if (source instanceof CarArrivalLink) {
                            CarArrivalLink evento = (CarArrivalLink) source;
                            evento.putStreetAgent(this);
                            //evento = null;
                            myLogger.debug("Send " + evento + " to be executed by pollThread");
                            isReady = false;
                            pool.execute(evento);

                        }
                        if (source instanceof CarLeaveLink) {
                            CarLeaveLink evento = (CarLeaveLink) source;
                            evento.putStreetAgent(this);
                            //evento = null;
                            myLogger.debug("Send " + evento + " to be executed by pollThread");
                            isReady = false;
                            pool.execute(evento);

                        }
                        if (source instanceof CarStart) {
                            CarStart evento = (CarStart) source;
                            evento.putStreetAgent(this);
                            //evento = null;
                            myLogger.debug("Send " + evento + " to be executed by pollThread");
                            isReady = false;
                            pool.execute(evento);

                        }
                        if (source instanceof CarStop) {
                            CarStop evento = (CarStop) source;
                            evento.putStreetAgent(this);
                            //evento = null;
                            myLogger.debug("Send " + evento + " to be executed by pollThread");
                            isReady = false;
                            pool.execute(evento);

                        }
                        if (source instanceof LightChangeEvent) {
                            LightChangeEvent evento = (LightChangeEvent) source;
                            evento.putStreetAgent(this);
                            //evento = null;
                            myLogger.debug("Send " + evento + " to be executed by pollThread");
                            isReady = false;
                            pool.execute(evento);

                        }


                        myLogger.info("The streetController is ready:" + isReady);


                    }

                } else {
                    // service.blockingServeOldest();
                }
            }




        } catch (InterruptedException ix) {
            ix.printStackTrace();
        }

    }

    public void putEvent(int segment, double evTime, int typeEvent, Object objectId, double eventPosition, int desiredStatus, int lane) {
        // controler.receiveMessage("New time> " + evTime + "type" + typeEvent);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream("c:/temp/logOrdenado.txt", true);
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
        }

        /*
         *String objectID;
        double evTime;
        int segment;
        double evPos;
        int evType;
         */

        PrintWriter miArchivo = new PrintWriter(out);
        miArchivo.write("" + segment);
        miArchivo.write("," + evTime);
        miArchivo.write("," + typeEvent);
        miArchivo.write("," + objectId);
        miArchivo.write("," + eventPosition);

        miArchivo.println();
        miArchivo.close();
        if (segment != -1) {
            this.segments[segment].agregarEvento(evTime, typeEvent, objectId, eventPosition, desiredStatus, lane);
        }
    }

    /**
     * Retornar el siguiente evento a ser ejecutado
     * Requerimiento: 3.2.SCA.3.1
     * @return El proximo evento a ser ejecutado, por polimorfismo en tiempo de ejecuci�n se detectar� que tipo de evento es.
     * Los campos de siguiente,anterior, izquierda y derecha son puestos a null
     */
    public Event sacarEvento() {

        Event proximoEvento = rootControl.derecho;
        Event z = null;
        Event x = rootControl.derecho.siguiente;

        /* Nos aseguramos que no sea el SQS
        del segmento el que quedaria en lugar es decir este evento
        ya es el ultimo de la lista del sqs.
         */

        if (!x.getName().equals("SQS")) {
            z = rootControl.derecho;

            while (x.evTime() >= z.derecho.evTime() && z.derecho != rootControl) {
                z = z.derecho;
            }

            x.eventoPotencial = true;
            x.izquierdo = z;
            z.derecho.izquierdo = x;
            x.derecho = z.derecho;
            z.derecho = x;
        }

        /*
        sacamos al proximoEvento del circulo del rootControl
         */

        rootControl.setTime(proximoEvento.evTime());
        proximoEvento.anterior.setTime(proximoEvento.evTime());
        proximoEvento.izquierdo.derecho = proximoEvento.derecho;
        proximoEvento.derecho.izquierdo = proximoEvento.izquierdo;

        /*
         *Hacemos que el evento que apuntaba al evento proximo
         *apunte al siguiente al que apuntaba evento proximo
         *sustituyendo su espacio
         */

        proximoEvento.anterior.siguiente = proximoEvento.siguiente;
        proximoEvento.siguiente.anterior = proximoEvento.anterior;
        /*
         *Inicializamos el proximo evento todos sus campos de referencia
         *izquierda, derecha, etc, a null. Y hacemos su campo
         *de evento potencial sea false.
         */
        proximoEvento.anterior = null;
        proximoEvento.siguiente = null;
        proximoEvento.eventoPotencial = false;
        proximoEvento.derecho = null;
        proximoEvento.izquierdo = null;

        /* retornamos
         *el proximo evento a ser ejecutado
         */
        return proximoEvento;
    }

    public static void main(String args[]) {
        try {
            StreetControllerAgent sca = new StreetControllerAgent();
            sca.initSimulation();
        } catch (IOException ex) {
            myLogger.error("IOException");
            throw new UnsupportedOperationException("IOEXCEPTION");
        }
    }

    public void loadNetwork() {
    }
}
